using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace BrainPong
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        #region Variable Declaration
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public static Game1 game;
        public static Veld veld = new Veld(Vector3.Zero);
        public Ball ball;
        public Bat bat1, bat2;

        Vector3 cameraPosition = new Vector3(0.0f, 0.0f, 260.0f);
        float aspectRatio;
        Vector3 aimPosition = new Vector3(0.0f, 0.0f, 0.0f);

        MouseState currentMouse, prevMouse;
        int currentScroll, prevScroll = 0;
        KeyboardState prevKeys;

        float elapseTime;
        int FPS, frameCounter = 0;

        SpriteFont font;
        #endregion

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            ball = new Ball(Vector3.Zero, Vector3.Left,5);
            bat1 = new Bat(new Vector3(-20, 0, 0));
            bat2 = new Bat(new Vector3(20, 0, 0));
            game = this;
        }

        protected override void Initialize()
        {
            font = Content.Load<SpriteFont>("hudFont");
            base.Initialize();
        }

        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            Ball.loadModel();
            Bat.loadModel();
            Veld.loadModel();

        }

        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        protected override void Update(GameTime gameTime)
        {
            KeyboardState keyboardState = Keyboard.GetState();
            currentMouse = Mouse.GetState();
            currentScroll = currentMouse.ScrollWheelValue;
            int dScroll = prevScroll - currentScroll;

            elapseTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
            frameCounter++;

            if (elapseTime > 1)
            {
                FPS = frameCounter;
                frameCounter = 0;
                elapseTime = 0;
            }

            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) // Allows the game to exit
                this.Exit();
            if (keyboardState.IsKeyDown(Keys.Escape))
                this.Exit();

            #region camera transform
            if (dScroll < 0)
            {
                cameraPosition.Z -= 1;
                aimPosition.Z -= 1;
            }
            else if (dScroll > 0)
            {
                cameraPosition.Z += 1;
                aimPosition.Z += 1;
            }

            if (keyboardState.IsKeyDown(Keys.W) && !(prevKeys.IsKeyDown(Keys.W)))
            {
                cameraPosition.Y += 1;
                aimPosition.Y += 1;
            }
            if (keyboardState.IsKeyDown(Keys.S) && !(prevKeys.IsKeyDown(Keys.S)))
            {

                cameraPosition.Y -= 1;
                aimPosition.Y -= 1;
            }
            if (keyboardState.IsKeyDown(Keys.D) && !(prevKeys.IsKeyDown(Keys.D)))
            {

                cameraPosition.X += 1;
                aimPosition.X += 1;
            }
            if (keyboardState.IsKeyDown(Keys.A) && !(prevKeys.IsKeyDown(Keys.A)))
            {

                cameraPosition.X -= 1;
                aimPosition.X -= 1;
            }
            #endregion


            ball.updatePosition();

            bat1.UpdateInput(Keys.F1, Keys.F2);
            bat2.UpdateInput(Keys.Up, Keys.Down);

            prevMouse = currentMouse;
            prevScroll = currentScroll;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            aspectRatio = GraphicsDevice.Viewport.AspectRatio;

            ball.draw(this);
            bat1.draw(this);
            bat2.draw(this);
            veld.draw(this);

            spriteBatch.Begin();
            spriteBatch.DrawString(font, "cameraPosition ={" + cameraPosition.X + "," + cameraPosition.Y + "," + cameraPosition.Z + "}", new Vector2(10, 10), Color.White);
            spriteBatch.DrawString(font, "aimPosition ={" + aimPosition.X + "," + aimPosition.Y + "," + aimPosition.Z + "}", new Vector2(10, 25), Color.White);
            spriteBatch.DrawString(font, "FPS " + ((int)FPS).ToString(), new Vector2(10, 40), Color.White);
            spriteBatch.End();
            GraphicsDevice.BlendState = BlendState.Opaque;
            GraphicsDevice.DepthStencilState = DepthStencilState.Default;
            GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;

            base.Draw(gameTime);
        }


        public Model loadModel(string modelName)
        {
            return Content.Load<Model>("model\\" + modelName);
        }

        #region drawModel
        public void drawModel(Model model, Vector3 pos, Vector3 rot, Vector3 scale)
        {
            // Copy any parent transforms.
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

            Matrix view = Matrix.CreateLookAt(cameraPosition, aimPosition, Vector3.Up);
            Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(50.0f), aspectRatio, 1.0f, 10000.0f);

            Matrix rotate = Matrix.CreateRotationX(rot.X) * Matrix.CreateRotationY(rot.Y) * Matrix.CreateRotationZ(rot.Z);
            Matrix Scale = Matrix.CreateScale(scale);

            // Draw the model. A model can have multiple meshes, so loop.
            foreach (ModelMesh mesh in model.Meshes)
            {
                Matrix world = transforms[mesh.ParentBone.Index] * rotate * Matrix.CreateTranslation(pos) * Scale;

                // This is where the mesh orientation is set, as well 
                // as our camera and projection.
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.DirectionalLight1.Enabled = true;
                    effect.DirectionalLight1.Direction = new Vector3(0.0f, 300.0f, 0.0f);
                    effect.World = world;
                    effect.View = view;
                    effect.Projection = projection;
                }
                // Draw the mesh, using the effects set above.
                mesh.Draw();
            }
        }

        public void drawModel(Model model, Vector3 pos, Vector3 rot, float scale = 1.0f)
        {
            Vector3 Scale = new Vector3(scale);
            drawModel(model, pos, rot, Scale);
        }

        public void drawModel(Model model, Vector3 pos)
        {
            Vector3 Rot = Vector3.Zero;
            drawModel(model, pos, Rot);
        }
        #endregion
    }
}
